package org.zyj.io.time;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * 时间服务
 * 只要tcp连接上, 直接返回本机时间, 并关闭连接
 */
public class TimeServer {

    public static final int SERVER_PORT = 11226;

    public static void main(String[] args) throws Throwable {
        run();
    }

    public static void run() throws Throwable {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new TimeServerHandler());
                        }
                    })

                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture f = b.bind(SERVER_PORT).sync();

            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static class TimeServerHandler extends ChannelInboundHandlerAdapter {
        
        /**
         * 当连接建立的时候, 会调用此方法
         * 现在我们希望, 连接到server之后返回给客户端一个当前时间
         */
        @Override
        public void channelActive(final ChannelHandlerContext ctx) { // (1)
            //为了发送消息给客户端, 我们需要一个buffer来放消息
            //这里创建了一个4字节的buffer来放32位的int类型的数字
            ByteBufAllocator allocator = ctx.alloc();
            final ByteBuf time = allocator.buffer(4); // (2)


            time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));

            final ChannelFuture f = ctx.writeAndFlush(time); // (3)
            f.addListener(new ChannelFutureListener() {
                //由于netty中所有操作都是异步的, 所以, 不能直接close, 需要在真正的writeAndFlush完成之后, 再close()
                //所以在这注册一个回调
                @Override
                public void operationComplete(ChannelFuture future) {
                    assert f == future;
                    //当然这个close()也不一定会立即断开连接
                    ctx.close();
                }
            }); // (4)
            f.addListener(ChannelFutureListener.CLOSE); //这个和上面效果一样, 都是关闭连接

        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            System.out.println("exceptionCaught");
            cause.printStackTrace();
            ctx.close();
        }

    }
}
